package com.upb.webserver.node;

import cn.hutool.core.collection.CollectionUtil;
import com.upb.webserver.application.ApplicationManager;
import com.upb.webserver.chain.ChainManager;
import com.upb.webserver.common.enums.ChainTypeEnum;
import com.upb.webserver.common.enums.FiscoRemoteNodeTypeEnum;
import com.upb.webserver.common.exception.WebServerException;
import com.upb.webserver.common.pojo.node.RspNodeVO;
import com.upb.webserver.common.pojo.rest.fisco.chainmgr.resp.RespFront;
import com.upb.webserver.common.pojo.rest.fisco.chainmgr.resp.RespNode;
import com.upb.webserver.common.returncode.ConstantCode;
import com.upb.webserver.common.tools.JsonUtils;
import com.upb.webserver.dao.entity.TbChain;
import com.upb.webserver.dao.mapper.TbCompanyMapper;
import com.upb.webserver.dao.mapper.TbOrgMapper;
import com.upb.webserver.host.HostManager;
import com.upb.webserver.rest.fisco.FiscoChainMgrRestService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigInteger;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Slf4j
@Service
public class NodeService {
    @Autowired
    private ChainManager chainManager;
    @Autowired
    private ApplicationManager applicationManager;
    @Autowired
    private HostManager hostManager;
    @Autowired
    private FiscoChainMgrRestService fiscoChainMgrRestService;
    @Autowired
    private TbCompanyMapper companyMapper;
    @Autowired
    private TbOrgMapper orgMapper;


    /**
     * @param chainId
     * @param applicationId
     * @return
     */
    public List<RspNodeVO> queryNodeList(Integer chainId, Integer applicationId, Boolean enableState) {
        log.info("start exec method [queryNodeList]. chainId:{} groupId:{} enableState:{}", chainId, applicationId, enableState);

        TbChain tbChain = chainManager.verifyChainId(chainId);

        if (ChainTypeEnum.FISCO.getId() == tbChain.getChainType()) {
            return selectFiscoNodeList(chainId, applicationId);
        } else {
            log.warn("not support chainType:{}", tbChain.getChainType());
            throw new WebServerException(ConstantCode.CHAIN_TYPE_NOT_SUPPORT);
        }
    }


    /**
     * Select node list of chain and applicationId.
     *
     * @param chainId
     * @param applicationId
     * @return Map<K, V>  : K, nodeId; V, nodeType("remove","sealer","observer");
     */
    public List<RspNodeVO> selectFiscoNodeList(int chainId, Integer applicationId) {
        log.info("start exec method [selectFiscoNodeList]. chainId：{} applicationId：{}，", chainId, applicationId);

        int groupId = 0;
        if (applicationId != null && applicationId > 0) {
            groupId = applicationId;
        }

        List<RespFront> frontList = this.fiscoChainMgrRestService.selectFrontListByChainId(chainId, groupId);
        Map<String, RespNode> nodeMap = this.fiscoChainMgrRestService.selectNodeInfoList(chainId, groupId);
        if (CollectionUtils.isEmpty(frontList) || CollectionUtil.isEmpty(nodeMap)) {
            return Collections.emptyList();
        }

        Map<String, RespFront> respFrontMap = frontList.stream().collect(Collectors.toMap(RespFront::getNodeId, Function.identity()));
        Map<String, String> nodeTypeMap = this.fiscoChainMgrRestService.selectNodeConsensusTypeMap(chainId, groupId);


        List<RspNodeVO> nodeDtoList = new ArrayList<>(frontList.size());
        int finalGroupId = groupId;
        nodeMap.keySet().stream().forEach(nodeId -> {

            RspNodeVO rspNodeVO = new RspNodeVO();
            rspNodeVO.setChainId(chainId);
            rspNodeVO.setGroupId(finalGroupId);
            rspNodeVO.setNodeId(nodeId);

            RespNode node = nodeMap.get(nodeId);
            BigInteger blockHeight = node == null ? BigInteger.ZERO : BigInteger.valueOf(node.getBlockNumber());
            rspNodeVO.setBlockHeight(blockHeight);
            rspNodeVO.setPbftView(node == null ? 0 : node.getPbftView());

            FiscoRemoteNodeTypeEnum remoteTypeEnum = FiscoRemoteNodeTypeEnum.getById(nodeTypeMap.get(nodeId));
            if (remoteTypeEnum != null)
                rspNodeVO.setType(remoteTypeEnum.convertToNodeTypeEnum().getId());

            if (respFrontMap.containsKey(nodeId)) {
                RespFront respFront = respFrontMap.get(nodeId);
                rspNodeVO.setHostId(respFront.getExtHostId());
                rspNodeVO.setFrontId(respFront.getFrontId());
                rspNodeVO.setIp(respFront.getFrontIp());
                rspNodeVO.setPort(respFront.getFrontPort());
                rspNodeVO.setName(respFront.getContainerName());
                rspNodeVO.setStatus(respFront.getFrontStatus().byteValue());
                rspNodeVO.setBelongOrgName(hostManager.getOrgNameByHostId(respFront.getExtHostId()));
            }


            //get host info  TODO
//            TbHost tbHost = hostService.verifyHostId(respFront.getExtHostId());
//            rspNodeVO.setCreateCompanyId(tbHost.getCompanyId());
//            rspNodeVO.setCreateCompanyName(companyMapper.selectNameByPrimaryKey(tbHost.getCompanyId()));
//            rspNodeVO.setCreateOrgId(tbHost.getOrgId());
//            rspNodeVO.setCreateOrgName(orgMapper.selectNameByPrimaryKey(tbHost.getOrgId()));
            nodeDtoList.add(rspNodeVO);
        });

        log.info("success exec method [selectFiscoNodeList]. result：{}", JsonUtils.objToString(nodeDtoList));
        return nodeDtoList;
    }

}
